package com.joinway.framework.extension.console.pdf;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import com.joinway.framework.extension.console.cli.pdftk.PDFtkCommandTool;

public class PDFtkBuilder {

	private PDFtkCommandTool commandTool;
	
	private boolean debug;
	
	public PDFtkBuilder(PDFtkCommandTool tool){
		this.commandTool = tool;
	}
	
	public PDFtkBuilder(String pdftkPath){
		commandTool = new PDFtkCommandTool(pdftkPath);
	}
	
	public PDFFile buildPDFFile(String file) throws IOException, InterruptedException{
		return buildPDFFile(new File(file));
	}
	
	public PDFFile buildPDFFile(File file) throws IOException, InterruptedException{
		PDFFile pdfFile = new PDFFile(file);
		
		List<String> lines = constructDumpData(file.getAbsolutePath());
		int totalPageNumber = buildTotalPageNumber(lines);
		
		Map<String, List<String>> context = constructContext(lines);
		
		pdfFile.setTotalPageNumber(totalPageNumber);
		
		buildInfo(pdfFile, context);
		buildBookmark(pdfFile, context);
		
		pdfFile.setPlainParagraphs(buildPlainParagraphs(pdfFile.getParagraphs()));
		buildEndPageNumber(pdfFile.getPlainParagraphs(), totalPageNumber);
		
		return pdfFile;
	}
	
	private List<Paragraph> buildPlainParagraphs(List<Paragraph> paragraphs){
		List<Paragraph> plain = null;
		
		if(paragraphs != null){
			plain = new ArrayList<>();
			
			for(Paragraph p : paragraphs){
				plain.add(p);
				
				List<Paragraph> subs = buildPlainParagraphs(p.getParagraphs());
				if(subs != null && subs.size() > 0){
					plain.addAll(subs);
				}
			}
		}
		
		return plain;
	}
	
	private void buildEndPageNumber(List<Paragraph> plainParagraphs, int totalPageNumber){
		if(plainParagraphs != null){
			for(int i = 0; i < plainParagraphs.size(); i++){
				Paragraph p = plainParagraphs.get(i);
				Paragraph next = plainParagraphs.size() > i + 1 ? plainParagraphs.get(i + 1) : null;
				if(next != null){
					int endPageNumber = next.getStartPageNumber() - 1;
					p.setEndPageNumber(endPageNumber >= p.getStartPageNumber() ? endPageNumber : p.getStartPageNumber());
				}else{
					p.setEndPageNumber(totalPageNumber);
				}
			}
		}	
	}
	
	private void buildBookmark(PDFFile pdfFile, Map<String, List<String>> context){
		List<String> segments = context.get("BookmarkBegin");
		
		List<Paragraph> paragraphs = constructParagraphs(segments, 0, 1, null);
		pdfFile.setParagraphs(paragraphs);
	}
	
	private List<Paragraph> constructParagraphs(List<String> segments, int start, int depth, Paragraph parent){
		List<Paragraph> paragraphs = null;
		
		if(segments == null || segments.size() == 0 || start >= segments.size()){
			return paragraphs;
		}
		
		paragraphs = new ArrayList<>();
		
		Paragraph up = null;
		
		int i = start;
		while(i < segments.size()){
			Paragraph p = new Paragraph();
			
			int segCount = 3;
			for(int j = i; j < i + segCount; j++){
				PropertyContext pc = constructPropertyContext(segments.get(j));
				
				if("BookmarkTitle".equals(pc.getName())){
					p.setTitle(pc.getValue());
				}else if("BookmarkLevel".equals(pc.getName())){
					p.setLevel(Integer.valueOf(pc.getValue()));
				}else if("BookmarkPageNumber".equals(pc.getName())){
					p.setStartPageNumber(Integer.valueOf(pc.getValue()));
				}
			}
			
			if(p.getLevel() == depth){
				// 同级章节
				paragraphs.add(p);
			}else if(p.getLevel() > depth){
				// 子章节
				constructParagraphs(segments, i, p.getLevel(), up);
			}else{
				// 父章节
				parent.setParagraphs(paragraphs);
				break;
			}
			
			i += segCount;
			up = p;
		}
		
		return paragraphs;
	}
	
	private void buildInfo(PDFFile pdfFile, Map<String, List<String>> context){
		List<String> segments = context.get("InfoBegin");
		
		if(segments == null || segments.size() == 0){
			return;
		}
		
		int i = 0;
		while(i < segments.size()){
			String line = segments.get(i);
			PropertyContext pc = constructPropertyContext(line);
			
			String fieldName = null;
			if("InfoKey".equals(pc.getName())){
				fieldName = pc.getValue();
			}
			
			int next = i + 1;
			if(next >= segments.size()){
				break;
			}
			
			String value = constructPropertyContext(segments.get(next)).getValue();
			
			if("Creator".equals(fieldName)){
				pdfFile.setCreator(value);
			}else if("Title".equals(fieldName)){
				pdfFile.setTitle(value);
			}else if("Producer".equals(fieldName)){
				pdfFile.setProducer(value);
			}else if("Author".equals(fieldName)){
				pdfFile.setAuthor(value);
			}else if("ModDate".equals(fieldName)){
//				pdfFile.setModifyDate(modifyDate);
			}else if("CreationDate".equals(fieldName)){
				// 
			}
			
			i += 2;
		}
	}
	
	private PropertyContext constructPropertyContext(String line){
		PropertyContext pc = new PropertyContext();
		
		String[] values = StringUtils.split(line, ":");
		pc.setName(StringUtils.trim(values[0]));
		pc.setValue(values.length > 1 ? StringUtils.trim(values[1]) : "");
		
		return pc;
	}
	
	private Map<String, List<String>> constructContext(List<String> lines){
		Map<String, List<String>> context = new HashMap<>();

		List<String> entry = null;
		
		for(String line : lines){
			if(line.endsWith("Begin")){
				entry = context.get(line);
				
				if(entry == null){
					entry = new ArrayList<>();
					context.put(line, entry);
				}
			}else{
				entry.add(line);
			}
		}
		
		return context;
	}
	
	private List<String> constructDumpData(String file) throws IOException, InterruptedException{
		return commandTool.getDumpData(file);
	}
	
	private int buildTotalPageNumber(List<String> lines){
		int no = 0;
		
		for(String line : lines){
			if(line.startsWith("NumberOfPages")){
				String[] parts = line.split(":");
				
				if(parts == null || parts.length <= 1){
					errorDebugger("failed to get pdf file total page number");
					break;
				}
				
				if(parts[1] == null){
					errorDebugger("failed to get pdf file total page number");
					break;
				}
				
				no = Integer.valueOf(parts[1].trim());
				break;
			}
		}
		
		return no;
	}

	private void infoDebugger(String text){
		if(debug){
			System.out.println(getTimeStamp() + " " + text);
		}
	}
	
	private void errorDebugger(String text){
		if(debug){
			System.err.println(getTimeStamp() + " " + text);
		}
	}

	private String getTimeStamp(){
		return DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss,SSS");
	}

	public boolean isDebug() {
		return debug;
	}

	public void setDebug(boolean debug) {
		this.debug = debug;
	}
}

